﻿using DefCore.Core;
using DefCore.Interfaces;
using DefCore.Mapping;
using Ivory;
using Krypton;
using Krypton.Lights;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DefCore.Structures
{
    /// <summary>
    /// Architectural Control Unit
    /// </summary>
    public sealed class StructuralControlUnit : IRenderManager
    {
        public static BlueprintFrameAllowance? CurrentBlueprintAllowance { get; private set; }
        public static Blueprint BlueprintHolder;
        
        //Fields
        private List<Structure> _Structures;        
        private LevelBase _ParentLevel;

        public int RenderOrder
        {
            get { return 3; }
        }
        public RenderTarget2D Buffer
        {
            get;
            private set;
        }

        //ctor
        public StructuralControlUnit(LevelBase parentLevel)
        {
            this._ParentLevel = parentLevel;
            this._Structures = new List<Structure>();
        }

        //Methods
        private void CreateStructure(CellBase hoveredCell, StructureFrameEnum frame)
        {
            Structure temp = BlueprintHolder.CreateStructure(hoveredCell, frame);
            this.Add(temp);
            temp.Create();
        }
        
        public bool Load()
        {
            foreach (Structure s in _Structures)
                s.Load();

            return true;
        }
        public void Draw(SpriteBatch batch, GameTime time)
        {
            foreach (Structure s in _Structures)
                s.Render(batch, time);

            if (BlueprintHolder != null)
                BlueprintHolder.Render(batch, time);
        }
        public void Solve(InputControl control, GameTime time)
        {
            foreach (Structure s in _Structures)
                s.Solve(control, time);

            if (BlueprintHolder == null || _ParentLevel.Tool == EnumLevelToolsMode.NOTOOL)
            {
                CurrentBlueprintAllowance = null;
                return;
            }

            CurrentBlueprintAllowance = BlueprintHolder.AllowedFrameLocations;
            BlueprintHolder.Solve(control, time);

            CellBase hoveredCell = _ParentLevel.GameCellMap.FilterByVisible().Where(c => c.IsHoverTarget).SingleOrDefault();
            if (hoveredCell == null)
                BlueprintHolder.HitRect = null;
            else
            {
                if (!StructuralControlUnit.IsBlueprintValidForCell(hoveredCell))
                {
                    BlueprintHolder.HitRect = null;
                    return;
                }

                StructureFrameEnum frame = 0;
                //If the current blueprint is SEMIFRAME
                if (BlueprintHolder.FrameType == BlueprintFrameType.SEMIFRAME)
                    frame = hoveredCell.GetActiveFrame(control); //figure out which frame we are hovering over
                else //Current blueprint if FULLFRAME
                    frame = StructureFrameEnum.FULLFRAME;

                if (frame == 0)
                {
                    BlueprintHolder.HitRect = null;
                    return;
                }

                BlueprintHolder.HitRect = FrameManager.GetFrame(frame, hoveredCell);
                if (control.IsLeftClick())
                {
                    if ((frame & ConversionTools.FrameAllowanceToFrames(BlueprintHolder.AllowedFrameLocations)) == frame)
                    {
                        if (!BlueprintHolder.ValidatePlacement(hoveredCell))
                        {
                            //Alert the user through the Alerts! System
                            return;
                        }
                        
                        //See if we can palce it in that frame
                        if (!hoveredCell.PlaceFrame(frame))
                        {
                            return;
                        }
                        
                        //All checks out
                        CreateStructure(hoveredCell, frame);
                    }
                }
            }
        }

        public void Add(Structure structure)
        {
            _Structures.Add(structure);
            SelectionEngine.Instance.Selectables.Add(structure);
        }
        public void Remove(Structure structure, bool mute = false)
        {
            if(!mute)
                structure.Delete();

            _Structures.Remove(structure);
            structure.ParentCell.FreeFrame(structure.Frame); //FREE THE FRAME
            SelectionEngine.Instance.Selectables.Remove(structure);//REMOVE IT FROM THE SELECTIONENGINE
        }

        public void SetBlueprint(Blueprint blueprint)
        {
            if (blueprint == null)
            {
                //We are going out of Build mode
                BlueprintHolder = null;
                return;
            }

            BlueprintHolder = blueprint;
        }
        public void CreateBuffer(GameTime time)
        {
            throw new NotImplementedException("");
        }
        public void CheckFrames(int x, int y)
        {
            int i = 0;
            foreach (Point p in Globals.Directions)
            {
                Point nPosition = new Point(x + p.X, y + p.Y);

                if (!_ParentLevel.GameCellMap.Exists(nPosition.X, nPosition.Y))
                    continue;

                //Decide which frame we need to check
                StructureFrameEnum frameToCheck = 0;
                switch (i)
                {
                    case 0: frameToCheck = StructureFrameEnum.SOUTHFRAME; break;
                    case 1: frameToCheck = StructureFrameEnum.WESTFRAME; break;
                    case 2: frameToCheck = StructureFrameEnum.NORTHFRAME; break;
                    case 3: frameToCheck = StructureFrameEnum.EASTFRAME; break;

                    default: throw new DefenderException(DefCore.Errors.ACUStructureRemovalError);
                }

                //And check it
                Structure structToRemove = _Structures.Where(s => s.ParentCell.MapPosition == nPosition && s.Frame == frameToCheck).SingleOrDefault();
                if (structToRemove != null)
                {                    
                    _Structures.Remove(structToRemove);
                    this.Remove(structToRemove);
                }

                //inc
                i++;
            }
        }
                        
        public static bool IsBlueprintValidForCell(CellBase cell)
        {
            if (StructuralControlUnit.BlueprintHolder == null)
                return false;
            
            //CAN THE BLUEPRINT GO INTO THIS CELL?
            CellFrameType cellFrameType = cell.ValidFrameTypes;
            BlueprintFrameType blueprintFrameType = BlueprintHolder.FrameType;

            //COMPARE THESE AND RETURN IF THEY FIT
            CellFrameType blueprintAllocationToCellAllocation = (CellFrameType)(int)blueprintFrameType;

            return (cellFrameType & blueprintAllocationToCellAllocation) == blueprintAllocationToCellAllocation;
        }
        public static CellFrameType GetRenderableCellFrame(CellBase cell)
        {
            CellFrameType cellFrameType = cell.ValidFrameTypes;
            BlueprintFrameType blueprintFrameType = BlueprintHolder.FrameType;

            return (CellFrameType)(int)blueprintFrameType;
        }

        public IEnumerable<T> GetAllByType<T>()
            where T : Structure
        {
            return _Structures.Where(s => (s is T)).Select(c => (T)c);
        }

        public IEnumerable<Structure> GetAll()
        {
            return _Structures;
        }
        public IEnumerable<ShadowHull> GetHulls()
        {
            foreach (Structure s in _Structures.Where(s => s.EntityHull != null))
                yield return s.EntityHull;
        }
        public IEnumerable<ILight2D> GetLights()
        {
            foreach (Structure s in _Structures.Where(s => s.EntityLight != null))
                yield return s.EntityLight;
        }
    }
}
